/**
 * @file 三次方程求解，仅给出实根部分
 * @author mengke01(kekee000@gmail.com)
 *
 * see:
 * http://www.99cankao.com/algebra/cubic-equation.php
 *
 *  盛金公式判别法
 *  当A=B=0时，方程有一个三重实根。
 *  当Δ=B^2－4AC>0时，方程有一个实根和一对共轭虚根。
 *  当Δ=B^2－4AC=0时，方程有三个实根，其中有一个二重根。
 *  当Δ=B^2－4AC<0时，方程有三个不相等的实根。
 */

import quadraticEquation from './quadraticEquation';

/**
 * 求解三次方程
 *
 * @param {number} a a系数
 * @param {number} b b系数
 * @param {number} c c系数
 * @param {number} d d系数
 * @return {Array} 解数组，仅给出实根部分
 */

export default function cubeEquation(a, b, c, d) {
    if (a === 0) {
        return quadraticEquation(b, c, d);
    }

    if (d === 0) {
        return quadraticEquation(a, b, c);
    }

    if (a !== 1) {
        b /= a;
        c /= a;
        d /= a;
    }

    let disc;
    let q;
    let r;
    let dum1;
    let s;
    let t;
    let term1;
    let r13;

    q = (3.0 * c - (b * b)) / 9.0;
    r = -(27.0 * d) + b * (9.0 * c - 2.0 * (b * b));
    r /= 54.0;
    disc = q * q * q + r * r;
    term1 = (b / 3.0);

    // one root real, two are complex
    if (disc > 0) {
        s = r + Math.sqrt(disc);
        s = ((s < 0) ? -Math.pow(-s, (1.0 / 3.0)) : Math.pow(s, (1.0 / 3.0)));
        t = r - Math.sqrt(disc);
        t = ((t < 0) ? -Math.pow(-t, (1.0 / 3.0)) : Math.pow(t, (1.0 / 3.0)));

        return [-term1 + s + t];
    }

    // The remaining options are all real
    let r1;
    let r2;
    let r3;

    if (disc === 0) { // All roots real, at least two are equal.
        r13 = ((r < 0) ? -Math.pow(-r, (1.0 / 3.0)) : Math.pow(r, (1.0 / 3.0)));
        r1 = -term1 + 2.0 * r13;
        r2 = -(r13 + term1);
        r3 = r2;
        return r1 === r2 ? r1 : [r1, r2];
    }

    // Only option left is that all roots are real and unequal (to get here, q < 0)
    q = -q;
    dum1 = q * q * q;
    dum1 = Math.acos(r / Math.sqrt(dum1));
    r13 = 2.0 * Math.sqrt(q);
    r1 = -term1 + r13 * Math.cos(dum1 / 3.0);
    r2 = -term1 + r13 * Math.cos((dum1 + 2.0 * Math.PI) / 3.0);
    r3 = -term1 + r13 * Math.cos((dum1 + 4.0 * Math.PI) / 3.0);

    return [r1, r2, r3];
}
